DotNet Example: Digital Hash
Description
Digital hash functions are often used to validate executable files and digital documents. One important use of a digital hash is to sign e-commerce documents; this is required in order to do business on Amazon, for example.
The .NET Framework has a full set of encryption functions, so it was relatively easy to create a .NET assembly and Xbasic wrapper function for the purpose of implementing an HMAC (hash-based message authentication code).
The value returned for the HMAC hash is in Base64, which is what Amazon wants for e-commerce. If you try to compare Base64 with an online hash calculator that returns hexadecimal, they will not appear to match.
See the Plain SHA-1 Hash section below if you only want a simple SHA1 hash, rather than an encrypted HMAC hash based on SHA1 or another hash algorithm.
The C# code used to create the DLL, "DigitalHash.dll":
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Security.Cryptography; namespace HashLib { public class HMAC { /// <summary> /// Computes RFC 2104-compliant HMAC signature /// </summary> /// <param name="data"></param> /// <param name="key"></param> /// <param name="algorithm"></param> /// <returns>Signature</returns> public String Sign(String data, String key, KeyedHashAlgorithm algorithm) { Encoding encoding = new UTF8Encoding(); algorithm.Key = encoding.GetBytes(key); return Convert.ToBase64String(algorithm.ComputeHash( encoding.GetBytes(data.ToCharArray()))); } /// <summary> /// Computes RFC 2104-compliant HMAC signature using SHA1 /// </summary> /// <param name="data"></param> /// <param name="key"></param> /// <returns>Signature</returns> public String Sign1(String data, String key) { KeyedHashAlgorithm algorithm = new HMACSHA1(); return Sign(data, key, algorithm); } /// <summary> /// Computes RFC 2104-compliant HMAC signature using SHA256 /// </summary> /// <param name="data"></param> /// <param name="key"></param> /// <returns>Signature</returns> public String Sign256(String data, String key) { KeyedHashAlgorithm algorithm = new HMACSHA256(); return Sign(data, key, algorithm); } /// <summary> /// Computes RFC 2104-compliant HMAC signature using specified signature method /// </summary> /// <param name="data"></param> /// <param name="key"></param> /// <param name="SignatureMethod"></param> /// <returns>Signature</returns> public String SignUsing(String data, String key, String SignatureMethod) { KeyedHashAlgorithm algorithm = KeyedHashAlgorithm.Create(SignatureMethod.ToUpper()); return Sign(data, key, algorithm); } } }
The Xbasic module that contains the wrapper function:
FUNCTION HMAC_HASH as C(data as C, key as C, assembly_path as C, algorithm as C = "HMACSHA1") 'debug(1) dim alg as C = upper(algorithm) 'register .NET class dim sv as DotNet::Services dim assembly as DotNet::AssemblyReference assembly.FileName = assembly_path+"DigitalHash.dll" IF .not. file.exists(assembly.FileName) ui_msg_box("File not found",assembly.FileName) end END IF IF sv.RegisterClass("","","HashLib.HMAC",assembly) THEN 'Call appropriate .NET method if registration succeeds dim hash as DotNet::HashLib::HMAC SELECT CASE alg="HMACSHA1" HMAC_HASH = hash.Sign1(data,key) CASE alg="HMACSHA256" HMAC_HASH = hash.Sign256(data,key) CASE else HMAC_HASH = hash.SignUsing(data,key,alg) END SELECT END IF END FUNCTION exports.HMAC_HASH = HMAC_HASH
An interactive session to test the function:
dim xbm as p xbm = require("hmac_hash") path = "c:\path\to\DigitalHash\DLL\\" ? xbm.HMAC_HASH("my data", "my key", path) = "Wnw05dPAEo44+bw1luJqAWksvhE=" ? xbm.HMAC_HASH("my data", "my key", path, "HMACSHA1") = "Wnw05dPAEo44+bw1luJqAWksvhE=" ? xbm.HMAC_HASH("my data", "my key", path, "HMACSHA256") = "kBhEzgLKNjSjjzQw7s240hvoY62kDG/wHDjYXry++nA=" ? xbm.HMAC_HASH("my data", "my key", path, "HMACSHA384") = "1819IdbuGcIweTIhYBwIK1mOmNrlpgRKK98gnDlVJyXug36wQoDWuBoGlB/GfMqc" ? xbm.HMAC_HASH("my data", "my key", path, "MACTripleDES") ERROR: command: HMAC_HASH = hash.SignUsing(data,key,alg) Exception has been thrown by the target of an invocation. Specified key is not a valid size for this algorithm. ? xbm.HMAC_HASH("my data", "my key", path, "hMACmd5") = "6YeAf6EEZBgdF4BqkAQe/w==" ? xbm.HMAC_HASH("my data", "my key", path, "garbage") ERROR: command: HMAC_HASH = hash.SignUsing(data,key,alg) Exception has been thrown by the target of an invocation. Object reference not set to an instance of an object.
A modified version of the hmac_hash() function and the required .NET DLL are shipped with Alpha Anywhere.
Plain SHA-1 Hash
Calculating a plain SHA-1 hash is a simpler problem. The following script will accomplish the purpose.
dim data as b dim result as b data = "HMACSHA256" dim sham as ::System::Security::Cryptography::SHA1Managed = new ::System::Security::Cryptography::SHA1Managed() result = sham.ComputeHash(data) showvar(*to_hex(result))
This gives:
57568b047095c4679873f2ad36d9a76fe92fbc0c
See Also